// Irem M92 system games:
//
// port from MAME by OopsWare
//

#include "burnint.h"
#include "burn_ym2151.h"
#include "vez.h"


#include "irem_cpu.h"


static unsigned char *Mem = NULL, *MemEnd = NULL;
static unsigned char *RamStart, *RamEnd;

static unsigned char *RomV33;
static unsigned char *RomV30;
static unsigned char *RomGfx01;
static unsigned char *RomGfx02;
static unsigned char *RomSnd;

static unsigned char *RamVideo;
static unsigned char *RamV33;
static unsigned char *RamV30;
static unsigned char *RamSpr;
static unsigned char *RamSprCpy;
static unsigned char *RamPal;

static unsigned short *RamCurPal;
static unsigned short *VideoBuffer;

unsigned char m92Button[8] = {0, 0, 0, 0, 0, 0, 0, 0};
unsigned char m92Joy1[8] = {0, 0, 0, 0, 0, 0, 0, 0};
unsigned char m92Joy2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
unsigned char m92Joy3[8] = {0, 0, 0, 0, 0, 0, 0, 0};
unsigned char m92Joy4[8] = {0, 0, 0, 0, 0, 0, 0, 0};
unsigned char m92Input[8] = {0, 0, 0, 0, 0, 0, 0, 0};

unsigned char m92RecalcPalette = 0;
unsigned char m92Reset = 0;

static int m92_irq_vectorbase;
static unsigned int PalBank;

static unsigned char pf1_control[8],pf2_control[8],pf3_control[8],pf4_control[8];
static int pf1_enable,pf2_enable,pf3_enable;
static int pf1_shape,pf2_shape,pf3_shape;
static int pf1_rowscroll,pf2_rowscroll,pf3_rowscroll;
static int pf1_vram_ptr,pf2_vram_ptr,pf3_vram_ptr;
static int m92_sprite_list;

static unsigned char sound_status[2];
static unsigned char sound_latch[2];

static unsigned char irqvector;

static unsigned char m92_sprite_buffer_busy;
static int m92_sprite_buffer_timer;

static int m92_raster_irq_position = 0;
//static int m92_raster_enable = 1;

static inline unsigned int CalcCol(int offs)
{
	// xBBBBBGGGGGRRRRR
	int nColour = RamPal[offs + 0] | (RamPal[offs + 1] << 8);
	int r, g, b;

	r = (nColour & 0x001F) << 3;	// Red
	r |= r >> 5;
	g = (nColour & 0x03E0) >> 2;	// Green
	g |= g >> 5;
	b = (nColour & 0x7C00) >> 7;	// Blue
	b |= b >> 5;

	return BurnHighCol(r, g, b, 0);
}

// -----------------------------------------------------------
// Irem GA20 Sound chip
// -----------------------------------------------------------

#define MAX_VOL (256 >> 2)

static int sndratefrac;

static struct IremGA20_chip
{
	unsigned char regs[0x40];
	struct IremGA20_channel {
		unsigned int rate;
		unsigned int size;
		unsigned int start;
		unsigned int pos;
		unsigned int frac;
		unsigned int end;
		unsigned int volume;
		unsigned int pan;
		unsigned int effect;
		unsigned int play;
	} channel[4];
} * chip = 0;

unsigned char IremGA20_r(int offset)
{
	int chl = offset >> 4;
	switch (offset & 0xf) {
		case 0xe:	// voice status.  bit 0 is 1 if active. (routine around 0xccc in rtypeleo)
			return chip->channel[chl].play ? 1 : 0;

		default:
			//logerror("GA20: read unk. register %d, channel %d\n", offset & 0xf, channel);
			bprintf(PRINT_NORMAL, ("GA20 read of %08x chl(%d)\n"), offset, chl);
			break;
	}
	return 0;
}

void IremGA20_w(int offset, unsigned char data)
{
	chip->regs[offset] = data;
	int chl = offset >> 4;
	switch ( offset & 0xf ) {
		case 0x0:
			chip->channel[chl].start = ((chip->channel[chl].start)&0xff000) | (data<< 4);
			break;
		case 0x2:
			chip->channel[chl].start = ((chip->channel[chl].start)&0x00ff0) | (data<<12);
			break;
		case 0x4:
			chip->channel[chl].end = ((chip->channel[chl].end)&0xff000) | (data<< 4);
			break;
		case 0x6:
			chip->channel[chl].end = ((chip->channel[chl].end)&0x00ff0) | (data<<12);
			break;
		case 0x8:
			chip->channel[chl].rate = ( 0x1000000 / (256 - data) ) * sndratefrac;
			break;
		case 0xa:
			chip->channel[chl].volume = (data * MAX_VOL) / (data + 10);
			break;
		case 0xc:
			chip->channel[chl].play = data;
			chip->channel[chl].pos = chip->channel[chl].start;
			chip->channel[chl].end -= 0x20; // <<--- end prefix???
			chip->channel[chl].frac = 0;
			break;
		default:
			bprintf(PRINT_NORMAL, ("GA20 write %08x  %02x\n"), offset, data);
			break;
	}

}

int IremGA20_update(short * pBuffer, int len)
{
	if (!pBuffer) return len;

	for (int i=0; i<len; i++ ) {
		for(int chl=0; chl<4; chl++)
			if (chip->channel[chl].play) {
				if ( chip->channel[chl].pos < chip->channel[chl].end ) {
					short sample = RomSnd[ chip->channel[chl].pos ] - 0x80;
					sample *= chip->channel[chl].volume;

					chip->channel[chl].frac += chip->channel[chl].rate;
					chip->channel[chl].pos += chip->channel[chl].frac >> 24;
					chip->channel[chl].frac &= 0xffffff;

					pBuffer[0] += sample;
					pBuffer[1] += sample;

				} else
					chip->channel[chl].play = 0;
			}
		pBuffer += 2;
	}

	return len;
}

int IremGA20_reset()
{
	// 3579545Hz
	memset( chip, 0, sizeof( struct IremGA20_chip ) );
	sndratefrac = 14318180 / 16 / nBurnSoundRate;
	return 0;
}

// -----------------------------------------------------------
//
// -----------------------------------------------------------

static void snd_update_irq()
{
	if (irqvector & 0x2) {		/* YM2151 has precedence */
		VezSetIRQLine(0x18 * 4, VEZ_IRQSTATUS_ACK);
	} else if (irqvector & 0x1) {	/* V30 */
		VezSetIRQLine(0x19 * 4, VEZ_IRQSTATUS_ACK);
	}
}

static void m92YM2151IRQHandler(int nStatus)
{
	//bprintf(PRINT_NORMAL, ("m92YM2151IRQHandler nStatus %x\n"), nStatus);
	if (nStatus)irqvector |= 2;
	else		irqvector &= ~2;
}


unsigned char __fastcall m92ReadByte(unsigned int vezAddress)
{
	// Palette Read
	if ((vezAddress & 0xFF800) == 0xF8800 )
		return RamPal[ vezAddress - 0xF8800 + PalBank ];

	// V33 Start vector
	if ((vezAddress & 0xFFFF0) == 0xFFFF0 )
		return RomV33[ vezAddress - 0xFFFF0 + 0x7FFF0 ];

//	switch (vezAddress) {

//		default:
//			bprintf(PRINT_NORMAL, ("Attempt to read byte value of location %x\n"), vezAddress);
//	}
	return 0;
}

void __fastcall m92WriteByte(unsigned int vezAddress, unsigned char byteValue)
{
	static unsigned int sprite_extent;

	if ((vezAddress & 0xFF800) == 0xF8800 ) {
		RamPal[ vezAddress - 0xF8800 + PalBank ] = byteValue;
		if (vezAddress & 1) {
			int offs = (vezAddress - 0xF8800 + PalBank) >> 1;
			RamCurPal[ offs ] = CalcCol( offs << 1 );
		}
		return;
	}

	switch (vezAddress) {
		// 0xf9000 ~ 0xf900f : m92_spritecontrol_w
		case 0xF9000:
			sprite_extent = byteValue;
			break;
		case 0xF9004:
			if (byteValue==8)
				m92_sprite_list=(((0x100 - sprite_extent)&0xff)*8);
			else
				m92_sprite_list=0x800;
			break;
		case 0xF9008:
			/* Pixel clock is 26.6666 MHz, we have 0x800 bytes, or 0x400 words
	           to copy from spriteram to the buffer.  It seems safe to assume 1
	           word can be copied per clock.  So:

	           1 MHz clock would be 1 word every 0.000,001s = 1000ns
	           26.6666MHz clock would be 1 word every 0.000,000,037 = 37 ns
	           Buffer should copy in about 37888 ns. */

			m92_sprite_buffer_busy = 0;
			m92_sprite_buffer_timer = VezCurrentCPU->reg.nec_ICount - 341;	// 9000000 * 0x400 * 0.000000037;
			VezCurrentCPU->reg.nec_ICount = 341;

			//bprintf(PRINT_NORMAL, ("sprite buffer copy timer start %f\n"), 100.0 * VezSegmentCycles() / (9000000 / 60)  );

			//memcpy(RamSprCpy, RamSpr, 0x800);

			break;

		case 0xF9800:
			PalBank = (byteValue & 0x2 /* && m92_game_kludge!=3 */) ? 0x0800 : 0x0000;
			break;

		case 0xF9001:
		case 0xF9002:
		case 0xF9003:
		case 0xF9005:
		case 0xF9006:
		case 0xF9007:
		case 0xF9009:
		case 0xF900a:
		case 0xF900b:
		case 0xF9801:
			//if (!byteValue)
			break;
//		default:
			//if (vezAddress >= 0xA0000)
//			bprintf(PRINT_NORMAL, ("Attempt to write byte value %x to location %x\n"), byteValue, vezAddress);
	}
}

unsigned char __fastcall m92ReadPort(unsigned int vezPort)
{
	switch (vezPort) {
		case 0x00: return ~m92Input[0];	// player 1
		case 0x01: return ~m92Input[1];	// player 2
		case 0x03: return ~m92Input[7];	// dip 3
		case 0x04: return ~m92Input[6];	// dip 2
		case 0x05: return ~m92Input[5];	// dip 1
		case 0x06: return ~m92Input[2];	// player 3
		case 0x07: return ~m92Input[3];	// player 4

		case 0x08: return sound_status[0];
		case 0x09: return sound_status[1];

		case 0x88: return 0xFF;
		case 0x02: return (~m92Input[4] & 0x7F) | m92_sprite_buffer_busy;
//		default:
//			bprintf(PRINT_NORMAL, ("Attempt to read byte value of port %x\n"), vezPort);
	}
	return 0;
}

void __fastcall m92WritePort(unsigned int vezPort, unsigned char byteValue)
{
	switch (vezPort) {
		case 0x00:
			sound_latch[0] = byteValue;
			irqvector |= 1;
			break;
		case 0x01:
			sound_latch[1] = byteValue;
			break;

		case 0x02:
		case 0x03:
			//m92_coincounter_w

			break;

		case 0x20:
			// m92_bankswitch_w
			bprintf(PRINT_NORMAL, ("CPU 0 bank change to %02x\n"), byteValue);
			VezMapArea(0xa0000, 0xbffff, 0, RomV33 + 0x100000 + (byteValue&0x7)*0x10000);
			VezMapArea(0xa0000, 0xbffff, 2, RomV33 + 0x100000 + (byteValue&0x7)*0x10000);
			break;
		case 0x21:
			// m92_bankswitch_w
			break;

		case 0x40:
		case 0x41:
		case 0x42:
		case 0x43:
			// Interrupt controller, only written to at bootup
			break;
		// 0x80 ~ 0x87: m92_pf1_control_w
		case 0x80: pf1_control[0] = byteValue; break;
		case 0x81: pf1_control[1] = byteValue; break;
		case 0x82: pf1_control[2] = byteValue; break;
		case 0x83: pf1_control[3] = byteValue; break;
		case 0x84: pf1_control[4] = byteValue; break;
		case 0x85: pf1_control[5] = byteValue; break;
		case 0x86: pf1_control[6] = byteValue; break;
		case 0x87: pf1_control[7] = byteValue; break;
		// 0x88 ~ 0x8f: m92_pf2_control_w
		case 0x88: pf2_control[0] = byteValue; break;
		case 0x89: pf2_control[1] = byteValue; break;
		case 0x8a: pf2_control[2] = byteValue; break;
		case 0x8b: pf2_control[3] = byteValue; break;
		case 0x8c: pf2_control[4] = byteValue; break;
		case 0x8d: pf2_control[5] = byteValue; break;
		case 0x8e: pf2_control[6] = byteValue; break;
		case 0x8f: pf2_control[7] = byteValue; break;
		// 0x90 ~ 0x97: m92_pf3_control_w
		case 0x90: pf3_control[0] = byteValue; break;
		case 0x91: pf3_control[1] = byteValue; break;
		case 0x92: pf3_control[2] = byteValue; break;
		case 0x93: pf3_control[3] = byteValue; break;
		case 0x94: pf3_control[4] = byteValue; break;
		case 0x95: pf3_control[5] = byteValue; break;
		case 0x96: pf3_control[6] = byteValue; break;
		case 0x97: pf3_control[7] = byteValue; break;

		// 0x98 ~ 0x9f: m92_master_control_w
		case 0x98:
			pf4_control[0] = byteValue;
			pf1_enable = (byteValue & 0x10) ? 0 : 1;
			pf1_rowscroll = (byteValue & 0x40) >> 6;
			pf1_shape = (byteValue & 0x04) >> 2;
			pf1_vram_ptr = 0x4000 * (byteValue & 0x03);
			break;
		case 0x99: pf4_control[1] = byteValue; break;
		case 0x9a:
			pf4_control[2] = byteValue;
			pf2_enable = (byteValue & 0x10) ? 0 : 1;
			pf2_rowscroll = (byteValue & 0x40) >> 6;
			pf2_shape = (byteValue & 0x04) >> 2;
			pf2_vram_ptr = 0x4000 * (byteValue & 0x03);
			break;
		case 0x9b: pf4_control[3] = byteValue; break;
		case 0x9c:
			pf4_control[4] = byteValue;
			pf3_enable = (byteValue & 0x10) ? 0 : 1;
			pf3_rowscroll = (byteValue & 0x40) >> 6;
			pf3_shape = (byteValue & 0x04) >> 2;
			pf3_vram_ptr = 0x4000 * (byteValue & 0x03);
			break;
		case 0x9d: pf4_control[5] = byteValue; break;
		case 0x9e:
			pf4_control[6] = byteValue;
			//m92_raster_irq_position = ((pf4_control[7]<<8) | pf4_control[6]) - 128;
			break;
		case 0x9f:
			pf4_control[7] = byteValue;
			m92_raster_irq_position = ((pf4_control[7]<<8) | pf4_control[6]) - 128;
			//bprintf(PRINT_NORMAL, ("m92_raster_irq_position set to %d\n"), m92_raster_irq_position);
			break;

//		default:
//			bprintf(PRINT_NORMAL, ("Attempt to write byte value %x to port %x\n"), byteValue, vezPort);
	}

}

unsigned char __fastcall m92SndReadByte(unsigned int vezAddress)
{

	if (vezAddress >= 0xa8000 && vezAddress <= 0xa803f )
		return  IremGA20_r( vezAddress - 0xa8000 );

	switch (vezAddress) {

		case 0xa8042:
			return BurnYM2151ReadStatus();
		//case 0xa8043:
		//	YM2151_status_port_0_r
		//	break;

		case 0xa8044:
			return sound_latch[0];
		case 0xa8045:
			//return soundlatch_r(space, offset) | 0xff00;
			return 0xff;	//sound_latch[1];

		default:
			bprintf(PRINT_NORMAL, ("V30 Attempt to read byte value of location %x\n"), vezAddress);
	}
	return 0;
}

void __fastcall m92SndWriteByte(unsigned int vezAddress, unsigned char byteValue)
{
	if (vezAddress >= 0xa8000 && vezAddress <= 0xa803f ) {
		IremGA20_w( vezAddress - 0xa8000, byteValue );
		return;
	}

	if (vezAddress >= 0x9ff00 && vezAddress <= 0x9ffff ) {
		// NOP
		return;
	}

	switch (vezAddress) {
		case 0xa8040:
			BurnYM2151SelectRegister(byteValue);
			break;
		//case 0xa8041:
		//	YM2151_register_port_0_w
		//	break;

		case 0xa8042:
			BurnYM2151WriteRegister(byteValue);
			break;
		//case 0xa8043:
		//	YM2151_data_port_0_w
		//	break;

		case 0xa8044:
			irqvector &= ~1;
			break;
		//case 0xa8045:
		//	m92_sound_irq_ack_w
		//	break;

		case 0xa8046:
			// m92_sound_status_w
			sound_status[0] = byteValue;
			VezOpen(0);
			VezSetIRQLine(m92_irq_vectorbase + 12, VEZ_IRQSTATUS_ACK); // IRQ 3
			VezOpen(1);
			//bprintf(PRINT_NORMAL, ("m92_sound_status_w %x, and set M92_IRQ_3\n"), byteValue);
			break;
		case 0xa8047:
			sound_status[1] = byteValue;
			//bprintf(PRINT_NORMAL, ("m92_sound_status_w %x, and set M92_IRQ_3\n"), byteValue);
			break;

		default:
			if ( vezAddress > 0x1ffff)
				bprintf(PRINT_NORMAL, ("V30 Attempt to write byte value %x to location %x\n"), byteValue, vezAddress);
	}
}

static int DrvDoReset()
{
	VezOpen(0);
	VezReset();

	VezOpen(1);
	VezReset();

	BurnYM2151Reset();

	IremGA20_reset();

	memset(pf1_control, 0, sizeof(pf1_control));
	memset(pf2_control, 0, sizeof(pf2_control));
	memset(pf3_control, 0, sizeof(pf3_control));
	memset(pf4_control, 0, sizeof(pf4_control));

	pf1_enable = pf2_enable = pf3_enable = 0;
	pf1_shape = pf2_shape = pf3_shape = 0;
	pf1_rowscroll = pf2_rowscroll = pf3_rowscroll = 0;
	pf1_vram_ptr = pf2_vram_ptr = pf3_vram_ptr = 0;

	m92_sprite_buffer_busy = 0x80;
	m92_sprite_buffer_timer = 0;

	irqvector = 0;

	return 0;
}

// ------------------------------------------------------------------------------
// M92 Video
// ------------------------------------------------------------------------------

#define	M92_TILE_LAYER_1(xx)		\
	if (d[xx]) p[xx] = c | d[xx];	\

#define	M92_TILE_LAYER_1_LINE		\
	M92_TILE_LAYER_1(0)				\
	M92_TILE_LAYER_1(1)				\
	M92_TILE_LAYER_1(2)				\
	M92_TILE_LAYER_1(3)				\
	M92_TILE_LAYER_1(4)				\
	M92_TILE_LAYER_1(5)				\
	M92_TILE_LAYER_1(6)				\
	M92_TILE_LAYER_1(7)				\

#define	M92_TILE_LAYER_1_FX(xx)		\
	if (d[xx]) p[7-xx] = c | d[xx];	\

#define	M92_TILE_LAYER_1_LINE_FX	\
	M92_TILE_LAYER_1_FX(0)			\
	M92_TILE_LAYER_1_FX(1)			\
	M92_TILE_LAYER_1_FX(2)			\
	M92_TILE_LAYER_1_FX(3)			\
	M92_TILE_LAYER_1_FX(4)			\
	M92_TILE_LAYER_1_FX(5)			\
	M92_TILE_LAYER_1_FX(6)			\
	M92_TILE_LAYER_1_FX(7)			\

static void m92_tileLayer_1()
{
	/* T--- -YX-  Pccc cccc  tttt tttt tttt tttt */
	int offs, mx, my, x, y;
	signed short * pcontrol = (signed short *) &pf1_control[0];

	if ( pf1_rowscroll ) {

		short * scrolldata = (short *) & RamVideo[ 0xf400 + 0x400 * 0 ];

		for(offs=0,my=0; my<64; my++, scrolldata+=8) {

			y = (my * 8 - 128 - 8 - pcontrol[0]) & 0x1ff;
			if (y >= 240) y -= 512;

			if ( y<=-8 || y>= 240 ) { offs += 64 * 4; continue; }

			for (int k=0;k<8;k++) {

				for(mx=0; mx<64; mx++,offs+=4) {

					x = (mx * 8 - 80 + 0 - scrolldata[k]) & 0x1ff;
					if (x >= 320 ) x -= 512;
					//x = mx * 8 - ((80 - 0 + scrolldata[k] ) & 0x1ff);
					//if (x < -16) x += 512;

					if ( x<=-8 || x>=320 ) continue;

					int tile_index = offs + pf1_vram_ptr;
					unsigned int tileno = RamVideo[tile_index] | (RamVideo[tile_index+1] << 8) | ((RamVideo[tile_index+3] & 0x80) << 9);
					if (tileno == 0) continue;

					unsigned int c = (RamVideo[tile_index+2] & 0x7F) << 4;
					unsigned short * p = VideoBuffer + (y + k) * (16 + 320) + x;
					unsigned char * d = RomGfx01 + (tileno * 64) + k * 8;

					if ( RamVideo[tile_index+3] & 0x02 ) {
	 					M92_TILE_LAYER_1_LINE_FX
		 			} else {
	 					M92_TILE_LAYER_1_LINE
					}
				}
				offs-=4*64;
			}
			offs+=4*64;

		}

	} else {

		for(offs=0,my=0; my<64; my++) {

			//y = my * 8 - ((136 + pcontrol[0]) & 0x1ff);
			//if (y < -16) y += 512;
			y = (my * 8 - 128 - 8 - pcontrol[0]) & 0x1ff;
			if (y >= 240) y -= 512;

			if ( y<=-8 || y>= 240 ) { offs += 64 * 4; continue; }

			for(mx=0; mx<64; mx++,offs+=4) {

				//x = mx * 8 - ((80 - 0 + pcontrol[2] ) & 0x1ff);
				//if (x < -16) x += 512;
				x = (mx * 8 - 80 + 0 - pcontrol[2]) & 0x1ff;
				if (x >= 320 ) x -= 512;

				if ( x<=-8 || x>=320 ) continue;

				int tile_index = offs + pf1_vram_ptr;
				unsigned int tileno = RamVideo[tile_index] | (RamVideo[tile_index+1] << 8) | ((RamVideo[tile_index+3] & 0x80) << 9);
				if (tileno == 0) continue;

				unsigned int c = (RamVideo[tile_index+2] & 0x7F) << 4;
				unsigned short * p = VideoBuffer + y * (16 + 320) + x;
				unsigned char * d = RomGfx01 + (tileno * 64);

				if ( RamVideo[tile_index+3] & 0x02 )
					for (int k=0;k<8;k++) {
	 					M92_TILE_LAYER_1_LINE_FX
	 					d += 8;
	 					p += 16 + 320;
	 				}
	 			else
					for (int k=0;k<8;k++) {
	 					M92_TILE_LAYER_1_LINE
	 					d += 8;
	 					p += 16 + 320;
	 				}

			}

		}

	}

}


#define	M92_TILE_LAYER_2(xx)		\
	if (d[xx]) p[xx] = c | d[xx];	\

#define	M92_TILE_LAYER_2_LINE		\
	M92_TILE_LAYER_2(0)				\
	M92_TILE_LAYER_2(1)				\
	M92_TILE_LAYER_2(2)				\
	M92_TILE_LAYER_2(3)				\
	M92_TILE_LAYER_2(4)				\
	M92_TILE_LAYER_2(5)				\
	M92_TILE_LAYER_2(6)				\
	M92_TILE_LAYER_2(7)				\

#define	M92_TILE_LAYER_2_FX(xx)		\
	if (d[xx]) p[7-xx] = c | d[xx];	\

#define	M92_TILE_LAYER_2_LINE_FX	\
	M92_TILE_LAYER_2_FX(0)			\
	M92_TILE_LAYER_2_FX(1)			\
	M92_TILE_LAYER_2_FX(2)			\
	M92_TILE_LAYER_2_FX(3)			\
	M92_TILE_LAYER_2_FX(4)			\
	M92_TILE_LAYER_2_FX(5)			\
	M92_TILE_LAYER_2_FX(6)			\
	M92_TILE_LAYER_2_FX(7)			\

#define	M92_TILE_LAYER_2F(xx)		\
	if (d[xx]>7) p[xx] = c|d[xx];	\

#define	M92_TILE_LAYER_2F_LINE		\
	M92_TILE_LAYER_2F(0)			\
	M92_TILE_LAYER_2F(1)			\
	M92_TILE_LAYER_2F(2)			\
	M92_TILE_LAYER_2F(3)			\
	M92_TILE_LAYER_2F(4)			\
	M92_TILE_LAYER_2F(5)			\
	M92_TILE_LAYER_2F(6)			\
	M92_TILE_LAYER_2F(7)			\

#define	M92_TILE_LAYER_2F_FX(xx)	\
	if (d[xx]>7) p[7-xx] = c|d[xx];	\

#define	M92_TILE_LAYER_2F_LINE_FX	\
	M92_TILE_LAYER_2F_FX(0)			\
	M92_TILE_LAYER_2F_FX(1)			\
	M92_TILE_LAYER_2F_FX(2)			\
	M92_TILE_LAYER_2F_FX(3)			\
	M92_TILE_LAYER_2F_FX(4)			\
	M92_TILE_LAYER_2F_FX(5)			\
	M92_TILE_LAYER_2F_FX(6)			\
	M92_TILE_LAYER_2F_FX(7)			\

static void m92_tileLayer_2_b()
{
	/* T--- -YXM  Pccc cccc  tttt tttt tttt tttt */
	int offs, mx, my, x, y;
	signed short * pcontrol = (signed short *) &pf2_control[0];

	if ( pf2_rowscroll ) {

		short * scrolldata = (short *) & RamVideo[ 0xf400 + 0x400 * 1 ];

		for(offs=0,my=0; my<64; my++, scrolldata+=8) {
			y = (my * 8 - 128 - 8 - pcontrol[0]) & 0x1ff;
			if (y >= 240) y -= 512;
			if ( y<=-8 || y>= 240 ) { offs += 64 * 4; continue; }

			for (int k=0;k<8;k++) {

				for(mx=0; mx<64; mx++,offs+=4) {
					x = (mx * 8 - 80 + 2 - scrolldata[k]) & 0x1ff;
					if (x >= 320 ) x -= 512;
					if ( x<=-8 || x>=320 ) continue;
					int tile_index = offs + pf2_vram_ptr;

					if (RamVideo[tile_index+3] & 0x01) continue;

					unsigned int tileno = RamVideo[tile_index] | (RamVideo[tile_index+1] << 8) | ((RamVideo[tile_index+3] & 0x80) << 9);
					if (tileno == 0) continue;
					unsigned int c = (RamVideo[tile_index+2] & 0x7F) << 4;
					unsigned short * p = VideoBuffer + (y + k) * (16 + 320) + x;
					unsigned char * d = RomGfx01 + (tileno * 64) + k * 8;

					if ( RamVideo[tile_index+3] & 0x02 ) {
		 				M92_TILE_LAYER_2_LINE_FX
	 				} else {
		 				M92_TILE_LAYER_2_LINE
	 				}
				}
				offs-=4*64;
			}
			offs+=4*64;
		}

	} else {

		for(offs=0,my=0; my<64; my++) {
			y = (my * 8 - 128 - 8 - pcontrol[0]) & 0x1ff;
			if (y >= 240) y -= 512;
			if ( y<=-8 || y>= 240 ) { offs += 64 * 4; continue; }

			for(mx=0; mx<64; mx++,offs+=4) {
				x = (mx * 8 - 80 + 2 - pcontrol[2]) & 0x1ff;
				if (x >= 320 ) x -= 512;
				if ( x<=-8 || x>=320 ) continue;
				int tile_index = offs + pf2_vram_ptr;

				if (RamVideo[tile_index+3] & 0x01) continue;

				unsigned int tileno = RamVideo[tile_index] | (RamVideo[tile_index+1] << 8) | ((RamVideo[tile_index+3] & 0x80) << 9);
				if (tileno == 0) continue;
				unsigned int c = (RamVideo[tile_index+2] & 0x7F) << 4;
				unsigned short * p = VideoBuffer + y * (16 + 320) + x;
				unsigned char * d = RomGfx01 + (tileno * 64);

				if ( RamVideo[tile_index+3] & 0x02 )
					for (int k=0;k<8;k++) {
	 					M92_TILE_LAYER_2_LINE_FX
	 					d += 8;
	 					p += 16 + 320;
	 				}
	 			else
					for (int k=0;k<8;k++) {
	 					M92_TILE_LAYER_2_LINE
	 					d += 8;
	 					p += 16 + 320;
	 				}
			}
		}

	}
}

static void m92_tileLayer_2_f()
{
	/* T--- -YXM  Pccc cccc  tttt tttt tttt tttt */
	int offs, mx, my, x, y;
	signed short * pcontrol = (signed short *) &pf2_control[0];

	for(offs=0,my=0; my<64; my++) {
		y = (my * 8 - 128 - 8 - pcontrol[0]) & 0x1ff;
		if (y >= 240) y -= 512;
		if ( y<=-8 || y>= 240 ) { offs += 64 * 4; continue; }

		for(mx=0; mx<64; mx++,offs+=4) {
			x = (mx * 8 - 80 + 2 - pcontrol[2]) & 0x1ff;
			if (x >= 320 ) x -= 512;
			if ( x<=-8 || x>=320 ) continue;
			int tile_index = offs + pf2_vram_ptr;

			if ((RamVideo[tile_index+3] & 0x01) || !(RamVideo[tile_index+2] & 0x80)) continue;

			unsigned int tileno = RamVideo[tile_index] | (RamVideo[tile_index+1] << 8) | ((RamVideo[tile_index+3] & 0x80) << 9);
			if (tileno == 0) continue;
			unsigned int c = (RamVideo[tile_index+2] & 0x7F) << 4;
			unsigned short * p = VideoBuffer + y * (16 + 320) + x;
			unsigned char * d = RomGfx01 + (tileno * 64);

			if ( RamVideo[tile_index+3] & 0x02 )
				for (int k=0;k<8;k++) {
 					M92_TILE_LAYER_2F_LINE_FX
 					d += 8;
 					p += 16 + 320;
 				}
 			else
				for (int k=0;k<8;k++) {
 					M92_TILE_LAYER_2F_LINE
 					d += 8;
 					p += 16 + 320;
 				}
		}
	}
}

static void m92_tileLayer_2_m()
{
	/* T--- -YXM  Pccc cccc  tttt tttt tttt tttt */
	int offs, mx, my, x, y;
	signed short * pcontrol = (signed short *) &pf2_control[0];

	if ( pf2_rowscroll ) {

		short * scrolldata = (short *) & RamVideo[ 0xf400 + 0x400 * 1 ];

		for(offs=0,my=0; my<64; my++, scrolldata+=8) {
			y = (my * 8 - 128 - 8 - pcontrol[0]) & 0x1ff;
			if (y >= 240) y -= 512;
			if ( y<=-8 || y>= 240 ) { offs += 64 * 4; continue; }

			for (int k=0;k<8;k++) {

				for(mx=0; mx<64; mx++,offs+=4) {
					x = (mx * 8 - 80 + 2 - scrolldata[k]) & 0x1ff;
					if (x >= 320 ) x -= 512;
					if ( x<=-8 || x>=320 ) continue;
					int tile_index = offs + pf2_vram_ptr;

					if (!(RamVideo[tile_index+3] & 0x01)) continue;

					unsigned int tileno = RamVideo[tile_index] | (RamVideo[tile_index+1] << 8) | ((RamVideo[tile_index+3] & 0x80) << 9);
					if (tileno == 0) continue;
					unsigned int c = (RamVideo[tile_index+2] & 0x7F) << 4;
					unsigned short * p = VideoBuffer + (y + k) * (16 + 320) + x;
					unsigned char * d = RomGfx01 + (tileno * 64) + k * 8;

					if ( RamVideo[tile_index+3] & 0x02 ) {
		 				M92_TILE_LAYER_2_LINE_FX
	 				} else {
		 				M92_TILE_LAYER_2_LINE
	 				}
				}
				offs-=4*64;
			}
			offs+=4*64;
		}

	} else {

		for(offs=0,my=0; my<64; my++) {
			y = (my * 8 - 128 - 8 - pcontrol[0]) & 0x1ff;
			if (y >= 240) y -= 512;
			if ( y<=-8 || y>= 240 ) { offs += 64 * 4; continue; }

			for(mx=0; mx<64; mx++,offs+=4) {
				x = (mx * 8 - 80 + 2 - pcontrol[2]) & 0x1ff;
				if (x >= 320 ) x -= 512;
				if ( x<=-8 || x>=320 ) continue;
				int tile_index = offs + pf2_vram_ptr;

				if (!(RamVideo[tile_index+3] & 0x01)) continue;

				unsigned int tileno = RamVideo[tile_index] | (RamVideo[tile_index+1] << 8) | ((RamVideo[tile_index+3] & 0x80) << 9);
				if (tileno == 0) continue;
				unsigned int c = (RamVideo[tile_index+2] & 0x7F) << 4;
				unsigned short * p = VideoBuffer + y * (16 + 320) + x;
				unsigned char * d = RomGfx01 + (tileno * 64);

				if ( RamVideo[tile_index+3] & 0x02 )
					for (int k=0;k<8;k++) {
	 					M92_TILE_LAYER_2_LINE_FX
	 					d += 8;
	 					p += 16 + 320;
	 				}
	 			else
					for (int k=0;k<8;k++) {
	 					M92_TILE_LAYER_2_LINE
	 					d += 8;
	 					p += 16 + 320;
	 				}
			}
		}
	}
}

#define	M92_TILE_LAYER_3(xx)		\
	p[xx] = c | d[xx];				\

#define	M92_TILE_LAYER_3_LINE		\
	M92_TILE_LAYER_3(0)				\
	M92_TILE_LAYER_3(1)				\
	M92_TILE_LAYER_3(2)				\
	M92_TILE_LAYER_3(3)				\
	M92_TILE_LAYER_3(4)				\
	M92_TILE_LAYER_3(5)				\
	M92_TILE_LAYER_3(6)				\
	M92_TILE_LAYER_3(7)				\

static void m92_tileLayer_3()
{
	/* T--- -YX-  Pccc cccc  tttt tttt tttt tttt */
	int offs, mx, my, x, y;
	signed short * pcontrol = (signed short *) &pf3_control[0];

	if ( pf3_rowscroll ) {

		short * scrolldata = (short *) & RamVideo[ 0xf400 + 0x400 * 2 ];

		for(offs=0,my=0; my<64; my++, scrolldata+=8) {

			//y = my * 8 - ((136 + pcontrol[0]) & 0x1ff);
			//if (y < -16) y += 512;
			y = (my * 8 - 128 - 8 - pcontrol[0]) & 0x1ff;
			if (y >= 240) y -= 512;

			if ( y<=-8 || y>= 240 ) {
				offs += 64 * 4;
				continue;
			}

			for (int k=0;k<8;k++) {
				for(mx=0; mx<64; mx++,offs+=4) {
					//x = mx * 8 - ((80 - 4 + scrolldata[k] ) & 0x1ff);
					//if (x < -16) x += 512;
					x = (mx * 8 - 80 + 4 - scrolldata[k]) & 0x1ff;
					if (x >= 320 ) x -= 512;

					if ( x<=-8 || x>=320 ) continue;

					int tile_index = offs + pf3_vram_ptr;
					unsigned int tileno = RamVideo[tile_index] | (RamVideo[tile_index+1] << 8) | ((RamVideo[tile_index+3] & 0x80) << 9);
					//if (tileno == 0) continue;

					unsigned int c = (RamVideo[tile_index+2] & 0x7F) << 4;
					unsigned short * p = VideoBuffer + (y+k) * (16 + 320) + x;
					unsigned char * d = RomGfx01 + (tileno * 64) + k*8;

		 			M92_TILE_LAYER_3_LINE

		 		}
		 		offs-=4*64;
			}
			offs+=4*64;
		}

	} else

		for(offs=0,my=0; my<64; my++) {

			//y = my * 8 - ((136 + pcontrol[0]) & 0x1ff);
			//if (y < -16) y += 512;
			y = (my * 8 - 128 - 8 - pcontrol[0]) & 0x1ff;
			if (y >= 240) y -= 512;

			if ( y<=-8 || y>= 240 ) {
				offs += 64 * 4;
				continue;
			}

			for(mx=0; mx<64; mx++,offs+=4) {

				//x = mx * 8 - ((80 - 4 + pcontrol[2] ) & 0x1ff);
				//if (x < -16) x += 512;
				x = (mx * 8 - 80 + 4 - pcontrol[2]) & 0x1ff;
				if (x >= 320 ) x -= 512;

				if ( x<=-8 || x>=320 ) continue;

				int tile_index = offs + pf3_vram_ptr;
				unsigned int tileno = RamVideo[tile_index] | (RamVideo[tile_index+1] << 8) | ((RamVideo[tile_index+3] & 0x80) << 9);
				//if (tileno == 0) continue;

				unsigned int c = (RamVideo[tile_index+2] & 0x7F) << 4;
				unsigned short * p = VideoBuffer + y * (16 + 320) + x;
				unsigned char * d = RomGfx01 + (tileno * 64);

				for (int k=0;k<8;k++) {

	 				M92_TILE_LAYER_3_LINE

	 				d += 8;
	 				p += 16 + 320;
	 			}

			}

		}

}


#define M92_TILE_SPR_NORMAL(x)				\
	if (q[x]) p[x]=q[x]|color;				\

#define M92_TILE_SPR_FLIP_X(x)				\
	if (q[x]) p[15-x]=q[x]|color;			\

#define M92_TILE_SPR_NORMAL_LINE			\
	M92_TILE_SPR_NORMAL( 0)					\
	M92_TILE_SPR_NORMAL( 1)					\
	M92_TILE_SPR_NORMAL( 2)					\
	M92_TILE_SPR_NORMAL( 3)					\
	M92_TILE_SPR_NORMAL( 4)					\
	M92_TILE_SPR_NORMAL( 5)					\
	M92_TILE_SPR_NORMAL( 6)					\
	M92_TILE_SPR_NORMAL( 7)					\
	M92_TILE_SPR_NORMAL( 8)					\
	M92_TILE_SPR_NORMAL( 9)					\
	M92_TILE_SPR_NORMAL(10)					\
	M92_TILE_SPR_NORMAL(11)					\
	M92_TILE_SPR_NORMAL(12)					\
	M92_TILE_SPR_NORMAL(13)					\
	M92_TILE_SPR_NORMAL(14)					\
	M92_TILE_SPR_NORMAL(15)					\

#define M92_TILE_SPR_FLIP_X_LINE			\
	M92_TILE_SPR_FLIP_X( 0)					\
	M92_TILE_SPR_FLIP_X( 1)					\
	M92_TILE_SPR_FLIP_X( 2)					\
	M92_TILE_SPR_FLIP_X( 3)					\
	M92_TILE_SPR_FLIP_X( 4)					\
	M92_TILE_SPR_FLIP_X( 5)					\
	M92_TILE_SPR_FLIP_X( 6)					\
	M92_TILE_SPR_FLIP_X( 7)					\
	M92_TILE_SPR_FLIP_X( 8)					\
	M92_TILE_SPR_FLIP_X( 9)					\
	M92_TILE_SPR_FLIP_X(10)					\
	M92_TILE_SPR_FLIP_X(11)					\
	M92_TILE_SPR_FLIP_X(12)					\
	M92_TILE_SPR_FLIP_X(13)					\
	M92_TILE_SPR_FLIP_X(14)					\
	M92_TILE_SPR_FLIP_X(15)					\


static inline void m92_pdrawgfx(unsigned int code,unsigned int color,int flipx,int flipy,int sx,int sy)
{
	unsigned short * p	= (unsigned short *) VideoBuffer;
	unsigned char * q	= RomGfx02 + (code << 8);

	sx -= 80;
	sy -= 136;

	if ((sx <= -16) || (sx >= 320) || (sy <= -16) || (sy >= 240)) return;

	p += sy * ( 16 + 320) + sx;

	if (flipy) {
		p += (16 + 320) * 15;
		if (flipx)
			for (int i=0;i<16;i++) {
				M92_TILE_SPR_FLIP_X_LINE
				p -= (16 + 320);
				q += 16;
			}
		else
			for (int i=0;i<16;i++) {
				M92_TILE_SPR_NORMAL_LINE
				p -= (16 + 320);
				q += 16;
			}
	} else {
		if (flipx)
			for (int i=0;i<16;i++) {
				M92_TILE_SPR_FLIP_X_LINE
				p += (16 + 320);
				q += 16;
			}
		else
			for (int i=0;i<16;i++) {
				M92_TILE_SPR_NORMAL_LINE
				p += (16 + 320);
				q += 16;
			}
	}

}

static void m92_drawSprites( int xpri )
{
	/* -------x xxxxxxxx ------YX Pccccccc ssssssss ssssssss ---uuvvy yyyyyyyy */

	int offs = m92_sprite_list - 8;
	while (offs >= 0) {

		int x,y,sprite,colour,fx,fy,x_multi,y_multi,i,j,s_ptr,pri;

		y_multi=(RamSprCpy[offs+1]>>1)&0x3;
		x_multi=(RamSprCpy[offs+1]>>3)&0x3;
		y_multi=1 << y_multi; /* 1, 2, 4 or 8 */
		x_multi=1 << x_multi; /* 1, 2, 4 or 8 */

		//if ((RamSprCpy[offs+4]&0x80)==0x80) pri=0; else pri=2;
		pri = RamSprCpy[offs+4] & 0x80;

		if ( pri ^ xpri ) {
			offs -= 8 * x_multi;
			continue;
		}

		y=(RamSprCpy[offs+0] | (RamSprCpy[offs+1]<<8))&0x1ff;
		x=(RamSprCpy[offs+6] | (RamSprCpy[offs+7]<<8))&0x1ff;

		x = x - 16;
		y = 512 - 16 - y;

		sprite=(RamSprCpy[offs+2] | (RamSprCpy[offs+3]<<8));
		colour= (RamSprCpy[offs+4]&0x7f) << 4;

		//pri_sprite = (RamSprCpy[offs+1] & 0xe0) >> 5;

		fx=RamSprCpy[offs+5]&1;
		fy=(RamSprCpy[offs+5]&2)>>1;

		if (fx) x+=16 * (x_multi - 1);

		for (j=0; j<x_multi; j++) {
			s_ptr=8 * j;
			if (!fy) s_ptr+=y_multi-1;

			for (i=0; i<y_multi; i++) {

				m92_pdrawgfx(sprite + s_ptr, colour, fx, fy, x, y-i*16);

				if (fy) s_ptr++; else s_ptr--;
			}
			if (fx) x-=16; else x+=16;

			offs-=8;
		}
	}


}

void m92DrvDraw()
{
	// The VideoBuffer is (16 + 320) x (16 + 240 + 16) size of buffer
	// to store color index, for it larger then default vedio rect,
	// we can't safe render tile or sprites in to it without edge check

	if (pf3_enable) m92_tileLayer_3();
	else memset(VideoBuffer, 0, (16+320)*240*2);


	if (pf2_enable) m92_tileLayer_2_b();

	m92_drawSprites(0x00);

	if (pf2_enable) m92_tileLayer_2_f();
	if (pf2_enable) m92_tileLayer_2_m();

	if (pf1_enable) m92_tileLayer_1();

	m92_drawSprites(0x80);

	unsigned short * pdst = (unsigned short *) pBurnDraw;
	unsigned short * pscr = (unsigned short *) VideoBuffer;
	for (int j=0; j<240; j++, pscr += 16)
		for (int i=0; i<320; i++)
			*pdst++ = RamCurPal[ *pscr++ ];

#if 0
	bprintf(PRINT_NORMAL, ("E:%d%d%d S:%d%d%d P:%d%d%d %04x %04x %04x R: %04x %04x, %04x %04x, %04x %04x\n"),
		pf1_enable, pf2_enable, pf3_enable,
		pf1_rowscroll, pf2_rowscroll, pf3_rowscroll,
		pf1_shape, pf2_shape, pf3_shape,
		pf1_vram_ptr, pf2_vram_ptr, pf3_vram_ptr,
		(pf1_control[5]<<8)+pf1_control[4], (pf1_control[1]<<8) + pf1_control[0],
		(pf2_control[5]<<8)+pf2_control[4], (pf2_control[1]<<8) + pf2_control[0],
		(pf3_control[5]<<8)+pf3_control[4], (pf3_control[1]<<8) + pf3_control[0]  );
#endif
}

// ------------------------------------------------------------------------------
//
// ------------------------------------------------------------------------------

static int loadDecodeGfx01(unsigned char *tmp, int rid, int shift, int size)
{
	unsigned char * pgfx = RomGfx01;

	BurnLoadRom(tmp, rid, 1);

	for (int i=0; i<(size/8); i++) {
		for( int y=0;y<8;y++) {
			pgfx[0] |= ((tmp[0]>>7)&1)<<shift;
			pgfx[1] |= ((tmp[0]>>6)&1)<<shift;
			pgfx[2] |= ((tmp[0]>>5)&1)<<shift;
			pgfx[3] |= ((tmp[0]>>4)&1)<<shift;
			pgfx[4] |= ((tmp[0]>>3)&1)<<shift;
			pgfx[5] |= ((tmp[0]>>2)&1)<<shift;
			pgfx[6] |= ((tmp[0]>>1)&1)<<shift;
			pgfx[7] |= ((tmp[0]>>0)&1)<<shift;
			tmp ++;
			pgfx += 8;
		}
	}
	return 0;
}

static int loadDecodeGfx02(unsigned char *tmp, int rid, int shift, int size)
{
	unsigned char * pgfx = RomGfx02;

	BurnLoadRom(tmp, rid, 1);

	for (int i=0; i<(size/32); i++) {
		for( int y=0;y<16;y++) {
			pgfx[ 0] |= ((tmp[ 0]>>7)&1)<<shift;
			pgfx[ 1] |= ((tmp[ 0]>>6)&1)<<shift;
			pgfx[ 2] |= ((tmp[ 0]>>5)&1)<<shift;
			pgfx[ 3] |= ((tmp[ 0]>>4)&1)<<shift;
			pgfx[ 4] |= ((tmp[ 0]>>3)&1)<<shift;
			pgfx[ 5] |= ((tmp[ 0]>>2)&1)<<shift;
			pgfx[ 6] |= ((tmp[ 0]>>1)&1)<<shift;
			pgfx[ 7] |= ((tmp[ 0]>>0)&1)<<shift;

			pgfx[ 8] |= ((tmp[16]>>7)&1)<<shift;
			pgfx[ 9] |= ((tmp[16]>>6)&1)<<shift;
			pgfx[10] |= ((tmp[16]>>5)&1)<<shift;
			pgfx[11] |= ((tmp[16]>>4)&1)<<shift;
			pgfx[12] |= ((tmp[16]>>3)&1)<<shift;
			pgfx[13] |= ((tmp[16]>>2)&1)<<shift;
			pgfx[14] |= ((tmp[16]>>1)&1)<<shift;
			pgfx[15] |= ((tmp[16]>>0)&1)<<shift;

			tmp ++;
			pgfx += 16;
		}
		tmp += 16;
	}
	return 0;
}

static int MemIndex()
{
	unsigned char *Next; Next = Mem;
	RomV33 		= Next; Next += 0x0C0000;			// V33
	RomV30		= Next; Next += 0x020000 * 2;		// V30
	RomGfx01	= Next; Next += 0x200000;			// char
	RomGfx02	= Next; Next += 0x800000;			// spr
	RomSnd		= Next; Next += 0x080000;			// irem GA20

	RamStart	= Next;
	RamVideo	= Next; Next += 0x010000;
	RamV33		= Next; Next += 0x010000;
	RamV30		= Next; Next += 0x004000;
	RamSpr		= Next; Next += 0x000800;
	RamSprCpy	= Next; Next += 0x000800;
	RamPal		= Next; Next += 0x001000;			// 2 bank of 0x0800

	chip 		= (struct IremGA20_chip *) Next;
						Next += sizeof( struct IremGA20_chip );

	RamEnd		= Next;

	RamCurPal	= (unsigned short *) Next; Next += 0x001000;
	VideoBuffer	= (unsigned short *) Next; Next += (16 + 320) * (16 + 240 + 16) * 2 + 16;
	VideoBuffer += (16 + 320) * 16 + 16;

	MemEnd		= Next;
	return 0;
}

int m92hookInit()
{
	int ret;

	Mem = NULL;
	MemIndex();
	int nLen = MemEnd - (unsigned char *)0;
	if ((Mem = (unsigned char *)malloc(nLen)) == NULL) return 1;
	memset(Mem, 0, nLen);										// blank all memory
	MemIndex();

	ret = BurnLoadRom(RomV33 + 0x000001, 0, 2); if (ret != 0) return 1;
	ret = BurnLoadRom(RomV33 + 0x000000, 1, 2); if (ret != 0) return 1;
	ret = BurnLoadRom(RomV33 + 0x080001, 2, 2); if (ret != 0) return 1;
	ret = BurnLoadRom(RomV33 + 0x080000, 3, 2); if (ret != 0) return 1;

	ret = BurnLoadRom(RomV30 + 0x000001, 4, 2); if (ret != 0) return 1;
	ret = BurnLoadRom(RomV30 + 0x000000, 5, 2); if (ret != 0) return 1;
	irem_cpu_decrypt(0, hook_decryption_table, RomV30, RomV30+0x20000, 0x20000 );

	// load and decode tile
	unsigned char * tmp = (unsigned char *) malloc (0x100000);
	if ( tmp == 0 ) return 1;

	loadDecodeGfx01(tmp,  6, 0, 0x040000);
	loadDecodeGfx01(tmp,  7, 1, 0x040000);
	loadDecodeGfx01(tmp,  8, 2, 0x040000);
	loadDecodeGfx01(tmp,  9, 3, 0x040000);

	loadDecodeGfx02(tmp, 10, 0, 0x100000);
	loadDecodeGfx02(tmp, 11, 1, 0x100000);
	loadDecodeGfx02(tmp, 12, 2, 0x100000);
	loadDecodeGfx02(tmp, 13, 3, 0x100000);

	free(tmp);

	ret = BurnLoadRom(RomSnd, 14, 1); if (ret != 0) return 1;

	{
		unsigned int cpu_types[] = { 0, 8 };
		VezInit(2, &cpu_types[0]);

	    VezOpen(0);

		VezMapArea(0x00000, 0x9ffff, 0, RomV33 + 0x00000);	// CPU 0 ROM
		VezMapArea(0x00000, 0x9ffff, 2, RomV33 + 0x00000);	// CPU 0 ROM

		VezMapArea(0xa0000, 0xbffff, 0, RomV33 + 0xa0000);	// rom bank
		VezMapArea(0xa0000, 0xbffff, 2, RomV33 + 0xa0000);	// rom bank

		VezMapArea(0xd0000, 0xdffff, 0, RamVideo);
		VezMapArea(0xd0000, 0xdffff, 1, RamVideo);

		VezMapArea(0xe0000, 0xeffff, 0, RamV33);			// system ram
		VezMapArea(0xe0000, 0xeffff, 1, RamV33);

		VezMapArea(0xf8000, 0xf87ff, 0, RamSpr);			// sprites ram
		VezMapArea(0xf8000, 0xf87ff, 1, RamSpr);

		VezSetReadHandler(m92ReadByte);
		VezSetWriteHandler(m92WriteByte);
		VezSetReadPort(m92ReadPort);
		VezSetWritePort(m92WritePort);

		VezOpen(1);

		VezMapArea(0x00000, 0x1ffff, 0, RomV30 + 0x00000);	// CPU 1 ROM
		VezMapArea(0x00000, 0x1ffff, 2, RomV30 + 0x20000, RomV30 + 0x00000);

		VezMapArea(0xa0000, 0xa3fff, 0, RamV30);			// system ram
		VezMapArea(0xa0000, 0xa3fff, 1, RamV30);

		// V30 Startup vector
		VezMapArea(0xff800, 0xfffff, 0, RomV30 + 0x1f800);
		VezMapArea(0xff800, 0xfffff, 2, RomV30 + 0x3f800, RomV30 + 0x1f800);

		VezSetReadHandler(m92SndReadByte);
		VezSetWriteHandler(m92SndWriteByte);

	}

	m92_irq_vectorbase = 0x80;
	PalBank	= 0;

	BurnYM2151Init(3579545, 50.0);		// 3.5795 MHz
	YM2151SetIrqHandler(0, &m92YM2151IRQHandler);

	DrvDoReset();
	return 0;
}

int m92hookExit()
{
	BurnYM2151Exit();

	VezExit();

	free(Mem);
	Mem = NULL;

	return 0;
}

int m92hookFrame()
{
	if (m92Reset) DrvDoReset();

	if (m92RecalcPalette) {
		for (int i=0; i<0x800;i++)
			RamCurPal[i] = CalcCol(i<<1);
		m92RecalcPalette = 0;
	}

	m92Input[0] = 0x00;
	m92Input[1] = 0x00;
	m92Input[2] = 0x00;
	m92Input[3] = 0x00;
	m92Input[4] = 0x00;

	for (int i=0; i<8; i++) {
		m92Input[0] |= (m92Joy1[i] & 1) << i;
		m92Input[1] |= (m92Joy2[i] & 1) << i;
		m92Input[2] |= (m92Joy3[i] & 1) << i;
		m92Input[3] |= (m92Joy4[i] & 1) << i;
		m92Input[4] |= (m92Button[i] & 1) << i;
	}

#define FRAME_SEGMENTS	32

	int nSoundBufferPos = 0;
	int nSegmentLength = nBurnSoundLen / FRAME_SEGMENTS;
	int nSegmentLeft = nBurnSoundLen - nSegmentLength * FRAME_SEGMENTS;

	for (int i=0; i<FRAME_SEGMENTS; i++ ) {
		VezOpen(0);
		VezRun(9000000 / 60 / FRAME_SEGMENTS);

		if ( m92_sprite_buffer_busy == 0 ) {
			m92_sprite_buffer_busy = 0x80;
			memcpy(RamSprCpy, RamSpr, 0x800);
			VezSetIRQLine((m92_irq_vectorbase + 4), VEZ_IRQSTATUS_ACK);
			VezRun( m92_sprite_buffer_timer );
		}

		VezOpen(1);
		VezRun(7159090 / 60 / FRAME_SEGMENTS);

		if (pBurnSoundOut) {
			short* pSoundBuf = pBurnSoundOut + (nSoundBufferPos << 1);
			if (i == (FRAME_SEGMENTS-1)) nSegmentLength += nSegmentLeft;
			BurnYM2151Render(pSoundBuf, nSegmentLength);
			//IremGA20_update(pBurnSoundOut, nBurnSoundLen);
			nSoundBufferPos += nSegmentLength;
		}
		snd_update_irq();
	}

	VezOpen(0);
	VezSetIRQLine((m92_irq_vectorbase + 0), VEZ_IRQSTATUS_ACK);

	IremGA20_update(pBurnSoundOut, nBurnSoundLen);

#undef FRAME_SEGMENTS

	if (pBurnDraw) m92DrvDraw();

	return 0;
}

int m92DrvScan(int nAction,int *pnMin)
{
	if ( pnMin ) *pnMin =  0x029671;

	struct BurnArea ba;

	if (nAction & ACB_MEMORY_RAM) {								// Scan all memory, devices & variables
		memset(&ba, 0, sizeof(ba));
		ba.Data	  = RamStart;
		ba.nLen	  = RamEnd-RamStart;
		ba.szName = "All Ram";
		BurnAcb(&ba);

		if (nAction & ACB_WRITE)
			m92RecalcPalette = 1;
	}

	if (nAction & ACB_DRIVER_DATA) {

		VezScan(nAction);										// Scan 68000 state

		SCAN_VAR(m92Input);
		SCAN_VAR(m92_irq_vectorbase);
		SCAN_VAR(PalBank);

		SCAN_VAR(pf1_control);
		SCAN_VAR(pf2_control);
		SCAN_VAR(pf3_control);
		SCAN_VAR(pf4_control);

		SCAN_VAR(pf1_enable);
		SCAN_VAR(pf2_enable);
		SCAN_VAR(pf3_enable);

		SCAN_VAR(pf1_shape);
		SCAN_VAR(pf2_shape);
		SCAN_VAR(pf3_shape);

		SCAN_VAR(pf1_rowscroll);
		SCAN_VAR(pf2_rowscroll);
		SCAN_VAR(pf3_rowscroll);

		SCAN_VAR(pf1_vram_ptr);
		SCAN_VAR(pf2_vram_ptr);
		SCAN_VAR(pf3_vram_ptr);

		SCAN_VAR(m92_sprite_list);

		//SCAN_VAR(sound_status);

		//SCAN_VAR(m92_sprite_buffer_busy);
		//SCAN_VAR(m92_sprite_buffer_timer);
	}

	return 0;
}

// ----------------------------

static int MemIndex2()
{
	unsigned char *Next; Next = Mem;
	RomV33 		= Next; Next += 0x0C0000;			// V33
	RomV30		= Next; Next += 0x020000 * 2;		// V30
	RomGfx01	= Next; Next += 0x400000;			// char
	RomGfx02	= Next; Next += 0x800000;			// spr
	RomSnd		= Next; Next += 0x080000;			// irem GA20

	RamStart	= Next;
	RamVideo	= Next; Next += 0x010000;
	RamV33		= Next; Next += 0x010000;
	RamV30		= Next; Next += 0x004000;
	RamSpr		= Next; Next += 0x000800;
	RamSprCpy	= Next; Next += 0x000800;
	RamPal		= Next; Next += 0x001000;			// 2 bank of 0x0800

	chip 		= (struct IremGA20_chip *) Next;
						Next += sizeof( struct IremGA20_chip );

	RamEnd		= Next;

	RamCurPal	= (unsigned short *) Next; Next += 0x001000;
	VideoBuffer	= (unsigned short *) Next; Next += (16 + 320) * (16 + 240 + 16) * 2 + 16;
	VideoBuffer += (16 + 320) * 16 + 16;

	MemEnd		= Next;
	return 0;
}

int m92inthuntInit()
{
	int ret;

	Mem = NULL;
	MemIndex2();
	int nLen = MemEnd - (unsigned char *)0;
	if ((Mem = (unsigned char *)malloc(nLen)) == NULL) return 1;
	memset(Mem, 0, nLen);										// blank all memory
	MemIndex2();

	ret = BurnLoadRom(RomV33 + 0x000001, 0, 2); if (ret != 0) return 1;
	ret = BurnLoadRom(RomV33 + 0x000000, 1, 2); if (ret != 0) return 1;
	ret = BurnLoadRom(RomV33 + 0x080001, 2, 2); if (ret != 0) return 1;
	ret = BurnLoadRom(RomV33 + 0x080000, 3, 2); if (ret != 0) return 1;

	ret = BurnLoadRom(RomV30 + 0x000001, 4, 2); if (ret != 0) return 1;
	ret = BurnLoadRom(RomV30 + 0x000000, 5, 2); if (ret != 0) return 1;
	irem_cpu_decrypt(0, inthunt_decryption_table, RomV30, RomV30+0x20000, 0x20000 );

	// load and decode tile
	unsigned char *tmp = (unsigned char *) malloc (0x100000);
	if ( tmp == 0 ) return 1;

	loadDecodeGfx01(tmp,  6, 0, 0x080000);
	loadDecodeGfx01(tmp,  7, 1, 0x080000);
	loadDecodeGfx01(tmp,  8, 2, 0x080000);
	loadDecodeGfx01(tmp,  9, 3, 0x080000);

	loadDecodeGfx02(tmp, 10, 0, 0x100000);
	loadDecodeGfx02(tmp, 11, 1, 0x100000);
	loadDecodeGfx02(tmp, 12, 2, 0x100000);
	loadDecodeGfx02(tmp, 13, 3, 0x100000);

	free(tmp);

	ret = BurnLoadRom(RomSnd, 14, 1); if (ret != 0) return 1;

	{
		unsigned int cpu_types[] = { 0, 8 };
		VezInit(2, &cpu_types[0]);

	    VezOpen(0);

		VezMapArea(0x00000, 0x9ffff, 0, RomV33 + 0x00000);	// CPU 0 ROM
		VezMapArea(0x00000, 0x9ffff, 2, RomV33 + 0x00000);

		VezMapArea(0xa0000, 0xbffff, 0, RomV33 + 0xa0000);	// rom bank
		VezMapArea(0xa0000, 0xbffff, 2, RomV33 + 0xa0000);

		VezMapArea(0xc0000, 0xcffff, 0, RomV33 + 0x00000);	// Mirror, Used by In The Hunt as protection
		VezMapArea(0xc0000, 0xcffff, 2, RomV33 + 0x00000);

		VezMapArea(0xd0000, 0xdffff, 0, RamVideo);
		VezMapArea(0xd0000, 0xdffff, 1, RamVideo);

		VezMapArea(0xe0000, 0xeffff, 0, RamV33);			// system ram
		VezMapArea(0xe0000, 0xeffff, 1, RamV33);

		VezMapArea(0xf8000, 0xf87ff, 0, RamSpr);			// sprites ram
		VezMapArea(0xf8000, 0xf87ff, 1, RamSpr);

		VezSetReadHandler(m92ReadByte);
		VezSetWriteHandler(m92WriteByte);
		VezSetReadPort(m92ReadPort);
		VezSetWritePort(m92WritePort);

		VezOpen(1);

		VezMapArea(0x00000, 0x1ffff, 0, RomV30 + 0x00000);	// CPU 1 ROM
		VezMapArea(0x00000, 0x1ffff, 2, RomV30 + 0x20000, RomV30 + 0x00000);

		VezMapArea(0xa0000, 0xa3fff, 0, RamV30);			// system ram
		VezMapArea(0xa0000, 0xa3fff, 1, RamV30);

		// V30 Startup vector
		VezMapArea(0xff800, 0xfffff, 0, RomV30 + 0x1f800);
		VezMapArea(0xff800, 0xfffff, 2, RomV30 + 0x3f800, RomV30 + 0x1f800);

		VezSetReadHandler(m92SndReadByte);
		VezSetWriteHandler(m92SndWriteByte);
	}

	m92_irq_vectorbase = 0x80;
	PalBank	= 0;

	BurnYM2151Init(3579545, 80.0);		// 3.5795 MHz
	YM2151SetIrqHandler(0, &m92YM2151IRQHandler);

	DrvDoReset();
	return 0;
}

int m92inthuntFrame()
{
	if (m92Reset) DrvDoReset();

	if (m92RecalcPalette) {
		for (int i=0; i<0x800;i++)
			RamCurPal[i] = CalcCol(i<<1);
		m92RecalcPalette = 0;
	}

	m92Input[0] = 0x00;
	m92Input[1] = 0x00;
	m92Input[2] = 0x00;
	m92Input[3] = 0x00;
	m92Input[4] = 0x00;

	for (int i = 0; i < 8; i++) {
		m92Input[0] |= (m92Joy1[i] & 1) << i;
		m92Input[1] |= (m92Joy2[i] & 1) << i;
		m92Input[2] |= (m92Joy3[i] & 1) << i;
		m92Input[3] |= (m92Joy4[i] & 1) << i;
		m92Input[4] |= (m92Button[i] & 1) << i;
	}

#define FRAME_SEGMENTS	256

	int nSoundBufferPos = 0;
	int nSegmentLength = nBurnSoundLen / FRAME_SEGMENTS;
	int nSegmentLeft = nBurnSoundLen - nSegmentLength * FRAME_SEGMENTS;

	for (int i=FRAME_SEGMENTS-1; i>=0; i--) {

		VezOpen(0);
		VezRun(9000000 / 60 / FRAME_SEGMENTS);

		if ( m92_sprite_buffer_busy == 0 ) {
			m92_sprite_buffer_busy = 0x80;
			memcpy(RamSprCpy, RamSpr, 0x800);
			VezSetIRQLine(m92_irq_vectorbase + 4, VEZ_IRQSTATUS_ACK);
			VezRun( m92_sprite_buffer_timer );
		}

		if (i == m92_raster_irq_position)
			VezSetIRQLine(m92_irq_vectorbase + 8, VEZ_IRQSTATUS_ACK); // IRQ 2
		else if (i == 249)
			VezSetIRQLine(m92_irq_vectorbase + 0, VEZ_IRQSTATUS_ACK); // IRQ 0

		VezOpen(1);
		VezRun(7159090 / 60 / FRAME_SEGMENTS);

		if (pBurnSoundOut) {
			short* pSoundBuf = pBurnSoundOut + (nSoundBufferPos << 1);
			if (i == 0) nSegmentLength += nSegmentLeft;
			BurnYM2151Render(pSoundBuf, nSegmentLength);
			nSoundBufferPos += nSegmentLength;
		}
		snd_update_irq();

	}

#undef FRAME_SEGMENTS

	IremGA20_update(pBurnSoundOut, nBurnSoundLen);

	if (pBurnDraw) m92DrvDraw();

	return 0;
}



int m92rtypeleoExit()
{
	BurnYM2151Exit();

	VezExit();

	free(Mem);
	Mem = NULL;

	return 0;
}

int m92rtypeleoInit()
{
	int ret;

	Mem = NULL;
	MemIndex2();
	int nLen = MemEnd - (unsigned char *)0;
	if ((Mem = (unsigned char *)malloc(nLen)) == NULL) return 1;
	memset(Mem, 0, nLen);										// blank all memory
	MemIndex2();

	ret = BurnLoadRom(RomV33 + 0x000001, 0, 2); if (ret != 0) return 1;
	ret = BurnLoadRom(RomV33 + 0x000000, 1, 2); if (ret != 0) return 1;
	ret = BurnLoadRom(RomV33 + 0x080001, 2, 2); if (ret != 0) return 1;
	ret = BurnLoadRom(RomV33 + 0x080000, 3, 2); if (ret != 0) return 1;

	ret = BurnLoadRom(RomV30 + 0x000001, 4, 2); if (ret != 0) return 1;
	ret = BurnLoadRom(RomV30 + 0x000000, 5, 2); if (ret != 0) return 1;

	// load and decode tile
	unsigned char *tmp = (unsigned char *) malloc (0x100000);
	if ( tmp == 0 ) return 1;

	loadDecodeGfx01(tmp,  6, 0, 0x080000);
	loadDecodeGfx01(tmp,  7, 1, 0x080000);
	loadDecodeGfx01(tmp,  8, 2, 0x080000);
	loadDecodeGfx01(tmp,  9, 3, 0x080000);

	loadDecodeGfx02(tmp, 10, 0, 0x100000);
	loadDecodeGfx02(tmp, 11, 1, 0x100000);
	loadDecodeGfx02(tmp, 12, 2, 0x100000);
	loadDecodeGfx02(tmp, 13, 3, 0x100000);

	free(tmp);

	{
		unsigned int cpu_types[] = { 0, 8 };
		VezInit(2, &cpu_types[0]);

		VezOpen(0);

		VezMapArea(0x00000, 0x9ffff, 0, RomV33 + 0x00000);	// CPU 0 ROM
		VezMapArea(0x00000, 0x9ffff, 2, RomV33 + 0x00000);	// CPU 0 ROM

		VezMapArea(0xa0000, 0xbffff, 0, RomV33 + 0xa0000);	// rom bank
		VezMapArea(0xa0000, 0xbffff, 2, RomV33 + 0xa0000);	// rom bank

		VezMapArea(0xd0000, 0xdffff, 0, RamVideo);
		VezMapArea(0xd0000, 0xdffff, 1, RamVideo);

		VezMapArea(0xe0000, 0xeffff, 0, RamV33);			// system ram
		VezMapArea(0xe0000, 0xeffff, 1, RamV33);

		VezMapArea(0xf8000, 0xf87ff, 0, RamSpr);			// sprites ram
		VezMapArea(0xf8000, 0xf87ff, 1, RamSpr);

		VezSetReadHandler(m92ReadByte);
		VezSetWriteHandler(m92WriteByte);
		VezSetReadPort(m92ReadPort);
		VezSetWritePort(m92WritePort);

	}

	m92_irq_vectorbase = 0x20;
	PalBank	= 0;

	BurnYM2151Init(3579545, 80.0);		// 3.5795 MHz
	YM2151SetIrqHandler(0, &m92YM2151IRQHandler);

	DrvDoReset();
	return 0;
}


int m92rtypeleoFrame()
{

	if (m92Reset) DrvDoReset();

	if (m92RecalcPalette) {
		for (int i=0; i<0x800;i++)
			RamCurPal[i] = CalcCol(i<<1);
		m92RecalcPalette = 0;
	}

	m92Input[0] = 0x00;
	m92Input[1] = 0x00;
	m92Input[2] = 0x00;
	m92Input[3] = 0x00;
	m92Input[4] = 0x00;

	for (int i=0; i<8; i++) {
		m92Input[0] |= (m92Joy1[i] & 1) << i;
		m92Input[1] |= (m92Joy2[i] & 1) << i;
		m92Input[2] |= (m92Joy3[i] & 1) << i;
		m92Input[3] |= (m92Joy4[i] & 1) << i;
		m92Input[4] |= (m92Button[i] & 1) << i;
	}

#if 1

	VezOpen(0);

//
	for (int i=255; i>=0; i--) {

		VezRun(9000000 / 60 / 256);

		if ( m92_sprite_buffer_busy == 0 ) {
			m92_sprite_buffer_busy = 0x80;
			memcpy(RamSprCpy, RamSpr, 0x800);
			VezSetIRQLine(m92_irq_vectorbase + 4, VEZ_IRQSTATUS_ACK);
			VezRun( m92_sprite_buffer_timer );
		}

		if (i == m92_raster_irq_position)
			VezSetIRQLine(m92_irq_vectorbase + 8, VEZ_IRQSTATUS_ACK); // IRQ 2
		else
		if (i == 249) {
			VezSetIRQLine(m92_irq_vectorbase + 0, VEZ_IRQSTATUS_ACK); // IRQ 0

		}
	}

#else

	for (int i=0; i<32; i++ ) {
		VezOpen(0);
		VezRun(9000000 / 60 / 32);
		if ( m92_sprite_buffer_busy == 0 ) {
			m92_sprite_buffer_busy = 0x80;
			memcpy(RamSprCpy, RamSpr, 0x800);
			VezSetIRQLine((m92_irq_vectorbase + 4), VEZ_IRQSTATUS_ACK);
			VezRun( m92_sprite_buffer_timer );
		}

		VezOpen(1);
		VezRun(7159090 / 60 / 32);
	}

	VezOpen(0);
	VezSetIRQLine((m92_irq_vectorbase + 0), VEZ_IRQSTATUS_ACK);

	VezOpen(1);

#endif

	if (pBurnDraw) m92DrvDraw();

	return 0;
}
